package com.ds.lens.data.common.util;

import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLLimit;
import com.alibaba.druid.sql.ast.expr.*;
import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause;
import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
import com.ds.lens.data.common.constant.AggregatorEnum;
import com.ds.lens.data.common.query.*;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Description:
 *
 * @author WeiShaoying
 * @date 2019-12-25
 */
public class MqlParseUtil {

    private MqlParseUtil() {
    }

    public static void parseLimit(Pair<Integer, Integer> pair, SQLLimit limitClause) {
        if (limitClause == null) {
            return;
        }
        int offset = limitClause.getOffset() == null ? 0 : Integer.valueOf(limitClause.getOffset().toString());
        int rowcount = Integer.valueOf(limitClause.getRowCount().toString());
        pair.setLeft(offset);
        pair.setRight(rowcount);
    }

    public static void parseGroupBy(List<String> groups, Pair<String, Long> interval, SQLSelectGroupByClause groupByClause) throws Exception {
        List<SQLExpr> groupByItems = groupByClause == null ? null : groupByClause.getItems();
        if (CollectionUtils.isEmpty(groupByItems)) {
            return;
        }
        for (SQLExpr sqlExpr : groupByItems) {
            if (sqlExpr instanceof SQLMethodInvokeExpr) {
                SQLMethodInvokeExpr invokeExpr = (SQLMethodInvokeExpr) sqlExpr;
                if (invokeExpr.getMethodName().equals(DateHistogramFunction.get().getFunctionName())) {
                    List<SQLExpr> params = invokeExpr.getParameters();
                    if (params.size() != 2 || !(params.get(0) instanceof SQLVariantRefExpr) ||
                            !(params.get(1) instanceof SQLCharExpr)) {
                        throw new Exception();
                    }
                    SQLVariantRefExpr param0 = (SQLVariantRefExpr) params.get(0);
                    if (!IntervalTimestampVariable.get().getText().equals(param0.getName())) {
                        throw new Exception();
                    }
                    SQLCharExpr param1 = (SQLCharExpr) params.get(1);
                    String value = param1.getText();
                    if (!value.matches("[1-9]+[0-9]*[mhd]")) {
                        throw new Exception();
                    }
                    TimeUnitEnum timeUnit = TimeUnitEnum.get(value.charAt(value.length() - 1));
                    if (timeUnit == null) {
                        throw new Exception();
                    }
                    int intervalNum = Integer.valueOf(value.substring(0, value.length() - 1));
                    interval.setLeft(IntervalTimestampVariable.get().getText());
                    interval.setRight(timeUnit.toMills(intervalNum));
                } else {
                    throw new Exception();
                }
            } else if (sqlExpr instanceof SQLIdentifierExpr) {
                groups.add(((SQLIdentifierExpr) sqlExpr).getName());
            } else {
                throw new Exception();
            }
        }
    }

    public static void parseConditions(Map<String, List<Object>> equalsOrInMap, Pair<Long, Long> timeRange, SQLExpr whereExpr) throws Exception {
        if (whereExpr == null) {
            throw new Exception();
        }
        if (whereExpr instanceof SQLBinaryOpExpr) {
            SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr) whereExpr;
            SQLExpr leftExpr = binaryOpExpr.getLeft();
            SQLExpr rightExpr = binaryOpExpr.getRight();
            SQLBinaryOperator operator = binaryOpExpr.getOperator();
            if (leftExpr instanceof SQLVariantRefExpr && QueryTimeStampVariable.get().getText().equals(((SQLVariantRefExpr) leftExpr).getName())) {
                if (!(rightExpr instanceof SQLIntegerExpr) && !(rightExpr instanceof SQLMethodInvokeExpr)) {
                    throw new Exception();
                }
                Long value = null;
                if (rightExpr instanceof SQLIntegerExpr) {
                    SQLIntegerExpr intExpr = (SQLIntegerExpr) rightExpr;
                    value = intExpr.getNumber().longValue();
                } else if (rightExpr instanceof SQLMethodInvokeExpr) {
                    SQLMethodInvokeExpr methodExpr = (SQLMethodInvokeExpr) rightExpr;
                    if (!DateTimeFunction.get().getFunctionName().equals(methodExpr.getMethodName())) {
                        throw new Exception();
                    }
                    SQLExpr expr = methodExpr.getParameters().get(0);
                    if (!(expr instanceof SQLCharExpr)) {
                        throw new Exception();
                    }
                    String param = ((SQLCharExpr) expr).getText();
                    char u = param.charAt(param.length() - 1);
                    long duration = 0;
                    if (param.length() > 1) {
                        duration = Long.valueOf(param.substring(0, param.length() - 1));
                    }
                    TimeUnitEnum unitEnum = TimeUnitEnum.get(u);
                    value = System.currentTimeMillis() / 60000 * 60000 + unitEnum.toMills(duration);
                }
                if (SQLBinaryOperator.LessThan.equals(operator)) {
                    timeRange.setRight(value);
                } else if (SQLBinaryOperator.LessThanOrEqual.equals(operator)) {
                    timeRange.setRight(value);
                } else if (SQLBinaryOperator.GreaterThan.equals(operator)) {
                    timeRange.setLeft(value);
                } else if (SQLBinaryOperator.GreaterThanOrEqual.equals(operator)) {
                    timeRange.setLeft(value);
                } else {
                    throw new Exception();
                }
            } else if (SQLBinaryOperator.Equality.equals(operator)) {
                if(!"1".equals(leftExpr.toString())){
                    List<Object> list = equalsOrInMap.getOrDefault(leftExpr.toString(), new ArrayList<>());
                    list.add(evaluateSingleValueExpr(rightExpr));
                    equalsOrInMap.put(leftExpr.toString(), list);
                }
            } else if (SQLBinaryOperator.BooleanAnd.equals(operator)) {
                parseConditions(equalsOrInMap, timeRange, leftExpr);
                parseConditions(equalsOrInMap, timeRange, rightExpr);
            } else {
                throw new Exception();
            }
        } else if (whereExpr instanceof SQLInListExpr) {
            SQLInListExpr inListExpr = (SQLInListExpr) whereExpr;
            if (inListExpr.isNot()) {
                throw new Exception();
            }
            List<Object> list = equalsOrInMap.getOrDefault(inListExpr.getExpr().toString(), new ArrayList<>());
            for (SQLExpr targetExpr : inListExpr.getTargetList()) {
                list.add(evaluateSingleValueExpr(targetExpr));
            }
            equalsOrInMap.put(inListExpr.getExpr().toString(), list);
        } else {
            throw new Exception();
        }
    }

    private static Object evaluateSingleValueExpr(SQLExpr targetExpr) throws Exception {
        if (targetExpr instanceof SQLNumericLiteralExpr) {
            return ((SQLNumericLiteralExpr) targetExpr).getNumber();
        } else if (targetExpr instanceof SQLCharExpr) {
            return ((SQLCharExpr) targetExpr).getText();
        } else {
            throw new Exception();
        }
    }

    public static void parseSelectItems(Pair<String, AggregatorEnum> pair, List<SQLSelectItem> selectItems, SQLSelectGroupByClause groupByClause) throws Exception {
        if (CollectionUtils.isEmpty(selectItems)) {
            throw new Exception();
        }
        List<SQLExpr> groupByItems = groupByClause == null ? null : groupByClause.getItems();
        List<SQLSelectItem> copySelectItem = new ArrayList<>(selectItems);
        if (!CollectionUtils.isEmpty(groupByItems)) {
            for (Iterator<SQLSelectItem> itr = copySelectItem.iterator(); itr.hasNext(); ) {
                SQLExpr selectExpr = itr.next().getExpr();
                for (SQLExpr groupExpr : groupByItems) {
                    if (groupExpr.equals(selectExpr)) {
                        itr.remove();
                    }
                }
            }
        }
        if (copySelectItem.size() != 1) {
            throw new Exception();
        }

        SQLExpr metricExpr = copySelectItem.get(0).getExpr();
        AggregatorEnum aggrEnum = null;
        SQLExpr metric = null;
        if (metricExpr instanceof SQLAggregateExpr) {
            SQLAggregateExpr aggrExpr = ((SQLAggregateExpr) metricExpr);
            List<SQLExpr> arguments = aggrExpr.getArguments();
            if (arguments == null || arguments.size() != 1) {
                throw new Exception();
            }
            aggrEnum = AggregatorEnum.valueOf(aggrExpr.getMethodName());
            if (aggrEnum == null) {
                throw new Exception();
            }
            metric = aggrExpr.getArguments().get(0);
        } else {
            metric = metricExpr;
        }
        pair.setLeft(metric.toString());
        pair.setRight(aggrEnum);
    }
}
